package com.hyw.trace.configuration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import com.hyw.trace.configuration.meta.IEventStorage;
import com.hyw.trace.configuration.meta.TraceEvent;
import com.hyw.trace.configuration.meta.http.HttpEventStorage;
import com.hyw.trace.configuration.meta.mq.binder.ZTraceSource;
import com.hyw.trace.configuration.meta.redis.RedisEventStorage;
import com.hyw.trace.configuration.sample.aspect.MultiTraceSourcesAspect;
import com.hyw.trace.configuration.sample.aspect.TraceSourceAspect;
import com.hyw.trace.configuration.sample.meta.mq.MQOutcome;
import com.hyw.trace.configuration.sampler.AbstractSamplerTemplate;
import com.hyw.trace.configuration.sampler.TraceSamplerTemplate;

@Configuration(proxyBeanMethods = false)
public class ZTraceClientAutoConfiguration {

  /**
   * 默认的MQ配置
   * 
   * @author hongyu
   */
  @Configuration(proxyBeanMethods = false)
  @ConditionalOnProperty(name = "ztrace.client.meta.send.type", havingValue = "MQ", matchIfMissing = true)
  @EnableBinding(value = { ZTraceSource.class })
  public static class MQBinderConfiguration {
    private static final Logger log = LoggerFactory.getLogger(MQBinderConfiguration.class);

    /**
     * @return 构造默认的MQ承载管道
     */
    @SuppressWarnings("rawtypes")
    @Bean
    @ConditionalOnProperty(name = "ztrace.client.meta.send.type", havingValue = "MQ", matchIfMissing = true)
    @ConditionalOnMissingBean(value = { IEventStorage.class })
    public IEventStorage send(@Autowired ZTraceSource traceSource) {
      log.info("Applicable approach 'MQ' is actived for ztrace client.And posting data to Topic:{}",
          ZTraceSource.OUTPUT);
      return new MQOutcome(traceSource.output());
    }
  }

  /**
   * HTTP采集端配置
   * 
   * @author hongyu
   */
  @Configuration(proxyBeanMethods = false)
  @ConditionalOnProperty(name = "ztrace.client.meta.send.type", havingValue = "HTTP")
  public static class HttpSamplerConfig {
    private static final Logger log = LoggerFactory.getLogger(HttpSamplerConfig.class);

    @Bean
    public IEventStorage<TraceEvent> send(@Autowired(required = false) RestTemplate restTemplate,
        @Value("${ztrace.client.meta.send.uri:http://localhost:8080/ztrace/collect}") String uri,
        @Value("${ztrace.client.meta.send.dubug:true}") Boolean debug) {
      log.info(
          "Applicable approach 'HTTP' is actived for ztrace client.And posting data to target URI:{} with debugable:{}",
          uri, debug);
      return new HttpEventStorage(restTemplate, uri, debug);
    }
  }

  /**
   * Redis采集端配置
   * 
   * @author hongyu
   */
  @Configuration(proxyBeanMethods = false)
  @ConditionalOnProperty(name = "ztrace.client.meta.send.type", havingValue = "REDIS")
  public static class RedisSamplerConfig {
    private static final Logger log = LoggerFactory.getLogger(RedisSamplerConfig.class);

    @Bean
    public IEventStorage<TraceEvent> send(RedisTemplate<String, String> redisTemplate,
        @Value("${ztrace.client.meta.redis.topic:ZTrace") String topic) {
      log.info("Applicable approach 'REDIS' is actived for ztrace client.And posting data to Topic:{}", topic);
      return new RedisEventStorage(redisTemplate, topic);
    }
  }

  @SuppressWarnings({ "rawtypes", "unchecked" })
  @Bean
  @ConditionalOnMissingBean(value = { AbstractSamplerTemplate.class })
  public AbstractSamplerTemplate traceSamplerTemplate(@Autowired IEventStorage outcomeStorage,
      @Autowired SamplerConfiguration samplerConfiguration) {
    return new TraceSamplerTemplate(outcomeStorage, samplerConfiguration);
  }

  @Bean
  public TraceSourceAspect traceSourceAspect(@Autowired AbstractSamplerTemplate traceSamplerTemplate) {
    return new TraceSourceAspect(traceSamplerTemplate);
  }

  @Bean
  public MultiTraceSourcesAspect multiTraceSourcesAspect(@Autowired AbstractSamplerTemplate traceSamplerTemplate) {
    return new MultiTraceSourcesAspect(traceSamplerTemplate);
  }

  /**
   * 用于配置采集器的相关参数, 目前作为框架内部配置之用. <br>
   * 线程队列 <br>
   * 需要skip的一些采集信息
   * 
   * @author hongyu
   */
  @Component
  public static class SamplerConfiguration {

    /**
     * 异步采样器的线程池配置参数之：线程存活时长，默认为1000。时间单位是毫秒
     */
    @Value("${ztrace.sampler.defalut.thread.keepalive:1000}")
    private long defaultThreadKeepalive;

    /**
     * 异步采样器的线程池配置参数之：核心线程数，默认为200
     */
    @Value("${ztrace.sampler.defalut.core.thread:200}")
    private int defaultCoreThread;

    /**
     * 异步采样器的线程池配置参数之：最大线程数，默认为400
     */
    @Value("${ztrace.sampler.defalut.max.thread:400}")
    private int defaultMaxThread;

    /**
     * 异步采样器的线程池配置参数之：最大等待队列，默认为5000
     */
    @Value("${ztrace.sampler.defalut.max.queue:5000}")
    private int defaultMaxQueue;

    /**
     * 采样器允许跳过处理的分组信息：默认为null，即不作过滤.<br>
     * 与{@link#typeSkipPattern}为或逻辑关系
     */
    @Value("${ztrace.sampler.group.skip-pattern:#{null}}")
    private String groupSkipPattern;

    /**
     * 采样器允许跳过处理的类型信息：默认为null，即不作过滤<br>
     * 与{@link#groupSkipPattern}为或逻辑关系
     */
    @Value("${ztrace.sampler.type.skip-pattern:#{null}}")
    private String typeSkipPattern;

    public long getDefaultThreadKeepalive() {
      return defaultThreadKeepalive;
    }

    public void setDefaultThreadKeepalive(long defaultThreadKeepalive) {
      this.defaultThreadKeepalive = defaultThreadKeepalive;
    }

    public String getGroupSkipPattern() {
      return groupSkipPattern;
    }

    public void setGroupSkipPattern(String groupSkipPattern) {
      this.groupSkipPattern = groupSkipPattern;
    }

    public String getTypeSkipPattern() {
      return typeSkipPattern;
    }

    public void setTypeSkipPattern(String typeSkipPattern) {
      this.typeSkipPattern = typeSkipPattern;
    }

    public int getDefaultCoreThread() {
      return defaultCoreThread;
    }

    public void setDefaultCoreThread(int defaultCoreThread) {
      this.defaultCoreThread = defaultCoreThread;
    }

    public int getDefaultMaxThread() {
      return defaultMaxThread;
    }

    public void setDefaultMaxThread(int defaultMaxThread) {
      this.defaultMaxThread = defaultMaxThread;
    }

    public int getDefaultMaxQueue() {
      return defaultMaxQueue;
    }

    public void setDefaultMaxQueue(int defaultMaxQueue) {
      this.defaultMaxQueue = defaultMaxQueue;
    }

  }
}
